//TBS Version 0.4
//TBSView: one logic for converting Model to a visual representation

package tbs.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.swing.JScrollBar;

import tbs.TBSGraphics;
import tbs.TBSUtils;
import tbs.graphanalysis.ConvexHull;
import tbs.graphanalysis.HullCollision;
import tbs.graphanalysis.OptimalHulls;
import tbs.model.AdminModel;
import tbs.model.admin.Student;
import tbs.properties.PropertyLoader;
import tbs.view.dropdown.SubDropDown;
import tbs.view.dropdown.SubDropDownType;

/**
 * TBSView contains the logic for rendering the information contained in the
 * data model.
 **/
public class AdminView extends TBSView {

	/**
	 * 8-byte serialization class ID generated by
	 * https://www.fourmilab.ch/hotbits/secure_generate.html
	 */
	private static final long serialVersionUID = 0xBB7D0BF0A83E3AF6L;

	private AdminModel model;

	private boolean hasStudentScroll = false;
	private JScrollBar studentBar;
	private int studentYOffset = 0;
	private Rectangle mainDropDown;
	private Map<SubDropDownType, Rectangle> subDropDowns;

	private boolean displayDropDownMenu = false;
	private boolean displayHullMenu = false;
	private boolean displayCollisionMenu = false;
	private boolean displayOptimalMenu = false;

	public AdminView(Graphics2D g2, AdminModel m) {
		super(true, m);
		model = m;
		int studentBarMax = TBSGraphics.studentNodeHeight
				* model.getStudents().size();
		studentBarMax += (model.getStudents().size() - 1)
				* TBSGraphics.ySpacing;
		if (studentBarMax > model.getApplet().getHeight()) {
			studentBar = new JScrollBar(JScrollBar.VERTICAL, 0, model
					.getApplet().getHeight(), 0, studentBarMax);
			studentBar.setBlockIncrement(TBSGraphics.studentNodeHeight
					+ TBSGraphics.ySpacing);
			add(studentBar, BorderLayout.WEST);
			hasStudentScroll = true;
		} else {
			studentBar = new JScrollBar();
		}
		mainDropDown = new Rectangle();
		subDropDowns = new HashMap<SubDropDownType, Rectangle>();
		for (SubDropDownType sddType : SubDropDownType.values())
			subDropDowns.put(sddType, new Rectangle());
		positionButtons(g2);
		positionModelElements(g2);
	}

	public boolean hasStudentScroll() {
		return hasStudentScroll;
	}

	public JScrollBar getStudentBar() {
		return studentBar;
	}

	public int getStudentYOffset() {
		return studentYOffset;
	}

	// sets the start of viewable tree area
	public void setStudentYOffset(int yo) {
		studentYOffset = yo;
	}

	public void setDisplayDropDownMenu(Boolean displayDropDownMenu) {
		this.displayDropDownMenu = displayDropDownMenu;
		if (!this.displayDropDownMenu)
			mainDropDown = new Rectangle();
	}

	public void setDisplayHullMenu(Boolean displayHullMenu) {
		this.displayHullMenu = displayHullMenu;
		if (!this.displayHullMenu)
			subDropDowns.put(SubDropDownType.HULL, new Rectangle());
		else {
			setDisplayDropDownMenu(true);
			displayCollisionMenu = false;
			displayOptimalMenu = false;
		}
	}

	public void setDisplayCollisionMenu(Boolean displayCollisionMenu) {
		if (!this.displayCollisionMenu
				&& this.displayCollisionMenu == displayCollisionMenu)
			return;
		this.displayCollisionMenu = displayCollisionMenu;
		if (!this.displayCollisionMenu)
			subDropDowns.put(SubDropDownType.COLLISION, new Rectangle());
		else {
			setDisplayDropDownMenu(true);
			displayHullMenu = false;
			displayOptimalMenu = false;
		}
	}

	public void setDisplayOptimalMenu(Boolean displayOptimalMenu) {
		this.displayOptimalMenu = displayOptimalMenu;
		if (!this.displayOptimalMenu)
			subDropDowns.put(SubDropDownType.OPTIMAL_HULL, new Rectangle());

		else {
			setDisplayDropDownMenu(true);
			displayHullMenu = false;
			displayCollisionMenu = false;
		}
	}

	public void displaySubDropDown(SubDropDownType type) {
		boolean displayMenu = false, displayHull = false, displayCollision = false, displayOptimal = false;
		if (type != null) {
			switch (type) {
			case HULL:
				displayHull = true;
				break;
			case COLLISION:
				displayCollision = true;
				break;
			case OPTIMAL_HULL:
				displayOptimal = true;
				break;
			}
			displayMenu = true;
		}
		setDisplayHullMenu(displayHull);
		setDisplayCollisionMenu(displayCollision);
		setDisplayOptimalMenu(displayOptimal);
		setDisplayDropDownMenu(displayMenu);
	}

	public void closeDropDowns() {
		displayHullMenu = false;
		displayCollisionMenu = false;
		displayOptimalMenu = false;
		displayDropDownMenu = false;
	}

	public Rectangle getMainDropDown() {
		return mainDropDown;
	}

	public Rectangle getSubDropDown(SubDropDownType type) {
		return subDropDowns.get(type);
	}

	/**
	 * Displays the button bar.
	 */
	public void renderButtons(Graphics2D g2) {
		if (!getScreenPrintMode()) {
			TBSButtonType buttonClicked = model.getController()
					.getButtonClicked();
			if (buttonClicked == null || model.getPrompt() == null)
				buttonClicked = TBSButtonType.TREE;
			int characterWidth = TBSGraphics.maxStudentNameWidth
					+ TBSGraphics.checkWidth + TBSGraphics.arrowWidth;
			int studentWidth = characterWidth + getVerticalBar().getWidth()
					+ (hasStudentScroll ? studentBar.getWidth() : 0);

			TBSGraphics.questionButtonsStart = (model.getApplet().getWidth() - studentWidth)
					/ 2
					+ (studentWidth - getVerticalBar().getWidth())
					- ((TBSGraphics.buttonsWidth * getButtons().size()) / 2);
			Rectangle buttonRect = new Rectangle(
					TBSGraphics.questionButtonsStart, 0,
					TBSGraphics.buttonsWidth, TBSGraphics.buttonsHeight);
			int upperY = TBSGraphics.buttonsHeight - TBSGraphics.padding.height;
			for (TBSButtonType b : getButtons()) {
				if (b.equals(buttonClicked))
					TBSGraphics.renderButtonBackground(g2, buttonRect, true);
				else
					TBSGraphics.renderButtonBackground(g2, buttonRect, false);
				g2.setColor(Color.gray);
				g2.draw(buttonRect);
				TBSGraphics.drawCenteredString(g2, b.toString(), buttonRect.x,
						upperY, buttonRect.width, 0);
				buttonRect.setLocation(buttonRect.x + TBSGraphics.buttonsWidth,
						buttonRect.y);
			}

			/*
			 * Render the item(s) that have been selected by the user from the
			 * sub drop-downs (hulls, collisions, optimal groups) now so that
			 * when the user hovers over the drop-down menu it will display
			 * above the item.
			 */
			List<ConvexHull> hulls = model.getHulls(true);
			List<HullCollision> collisions = model.getHullCollisions(true);
			List<OptimalHulls> optimals = model.getOptimalHulls(true);

			for (SubDropDown sdd : hulls) {
				if (sdd.getDisplay())
					sdd.render(g2, getXOffset(), getYOffset(), model);
			}
			for (SubDropDown sdd : collisions) {
				if (sdd.getDisplay())
					sdd.render(g2, getXOffset(), getYOffset(), model);
			}
			for (SubDropDown sdd : optimals) {
				if (sdd.getDisplay())
					sdd.render(g2, getXOffset(), getYOffset(), model);
			}
			mainDropDown.setLocation(model.getApplet().getWidth()
					- (TBSGraphics.groupsButtonWidth + getVerticalBar()
							.getWidth()), TBSGraphics.buttonsHeight);
			// Drop Down Menu Button
			buttonRect = new Rectangle(mainDropDown.x, 0,
					TBSGraphics.groupsButtonWidth, TBSGraphics.buttonsHeight);
			TBSGraphics.renderButtonBackground(g2, buttonRect, false);
			g2.setColor(Color.gray);
			g2.draw(buttonRect);
			TBSGraphics.drawCenteredString(g2, "Menu", buttonRect.x, upperY,
					buttonRect.width, 0);
			if (displayDropDownMenu) {
				mainDropDown.setSize(TBSGraphics.groupsButtonWidth,
						TBSGraphics.buttonsHeight
								* model.getCurrentDropDowns().size());

				upperY = drawDropDownButton(g2, upperY, buttonRect, "Print");
				upperY = drawDropDownButton(g2, upperY, buttonRect,
						"Names & Groups"
								+ (getDisplayAllTooltips() ? " \u2713" : ""));

				if (model.getCurrentDropDowns().size() >= 6) {
					upperY = drawDropDownButton(g2, upperY, buttonRect,
							"Group Colors");
					upperY = drawDropDownButton(g2, upperY, buttonRect,
							"Deselect All");
					upperY = drawDropDownButton(g2, upperY, buttonRect,
							"\u25C0 Groups (" + model.getHulls(true).size()
									+ ")");

					if (model.getCurrentDropDowns().size() == 7) {
						upperY = drawDropDownButton(g2, upperY, buttonRect,
								"\u25C0 Collisions ("
										+ model.getHullCollisions(true).size()
										+ ")");
					}
					drawDropDownButton(g2, upperY, buttonRect,
							"\u25C0 Optimal Groups ("
									+ model.getOptimalHulls(true).size()
									+ ")");
				}
			} else
				mainDropDown.setSize(0, 0);

			List<SubDropDown> displayedGroup = new LinkedList<SubDropDown>();
			SubDropDownType displayedType = null;
			if (displayHullMenu) {
				displayedGroup.addAll(hulls);
				displayedType = SubDropDownType.HULL;
			}
			if (displayCollisionMenu) {
				displayedGroup.addAll(collisions);
				displayedType = SubDropDownType.COLLISION;
			}
			if (displayOptimalMenu) {
				displayedGroup.addAll(optimals);
				displayedType = SubDropDownType.OPTIMAL_HULL;
			}
			if (!displayedGroup.isEmpty())
				renderSubDropDown(g2, displayedGroup, displayedType);

		}
	}

	private int drawDropDownButton(Graphics2D g2, int upperY,
			Rectangle buttonRect, String label) {
		upperY += TBSGraphics.buttonsHeight;
		buttonRect.setLocation(buttonRect.x, buttonRect.y
				+ TBSGraphics.buttonsHeight);
		TBSGraphics.renderButtonBackground(g2, buttonRect, false);
		g2.setColor(Color.gray);
		g2.draw(buttonRect);
		TBSGraphics.drawCenteredString(g2, label, buttonRect.x, upperY,
				buttonRect.width, 0);
		return upperY;
	}

	public void renderElements(Graphics2D g2) {
		/*
		 * Uncomment this line of code to start logging of model integrity
		 * model.checkElementsIntegrity();
		 */
		renderUnselectedModelElements(g2);
		if (getMaxX() > (model.getApplet().getWidth() - getVerticalBar()
				.getWidth())) {
			getHorizontalBar().setVisibleAmount(
					model.getApplet().getWidth() - getVerticalBar().getWidth());
			getHorizontalBar().setMaximum(getMaxX());
			if (!getScreenPrintMode())
				getHorizontalBar().setVisible(true);

		} else
			getHorizontalBar().setVisible(false);
		renderTooltip(g2);
		if (getScreenPrintMode())
			renderScreenPrintText(g2);
	}

	public void renderStudents(Graphics2D g2) {
		if (!getScreenPrintMode()) {
			String selectedStudentName = model.getStudent().getName();
			int x, y, width;
			int characterWidth = TBSGraphics.maxStudentNameWidth
					+ TBSGraphics.checkWidth + TBSGraphics.arrowWidth;
			width = TBSGraphics.maxStudentNameWidth - TBSGraphics.padding.width;
			for (Student student : model.getStudents()) {
				if (student.getName().equals(selectedStudentName))
					g2.setColor(TBSGraphics.selectedStudentColor);
				else
					g2.setColor(Color.WHITE);
				x = student.getAnchorPoint().x
						+ (hasStudentScroll ? studentBar.getWidth() : 0);
				y = student.getAnchorPoint().y - studentYOffset;
				g2
						.fillRect(x, y, characterWidth,
								TBSGraphics.studentNodeHeight);
				String studentIndicators = "";
				int indicatorsWidth = TBSGraphics.arrowWidth
						+ TBSGraphics.checkWidth;
				if (student.hasArrows())
					studentIndicators += " \u2192";
				String lastUpdate = student.getLastUpdate();
				if (!TBSUtils.isStringEmpty(lastUpdate))
					studentIndicators += " \u2713";
				if (studentIndicators.length() > 0)
					TBSGraphics.drawCenteredString(g2, studentIndicators, x
							+ width, y, indicatorsWidth
							+ TBSGraphics.padding.width,
							TBSGraphics.studentNodeHeight, Color.BLACK);
				y += TBSGraphics.padding.width;
				for (String nameString : student.getNodeName()) {
					TBSGraphics.drawCenteredString(g2, nameString, x
							+ TBSGraphics.padding.width, y, width,
							TBSGraphics.textHeight, Color.BLACK);
					y += TBSGraphics.textHeight;
				}
			}
		}
	}

	private void renderSubDropDown(Graphics2D g2,
			List<? extends SubDropDown> items, SubDropDownType type) {
		if (!getScreenPrintMode()) {
			if (model.getPrompt() == null || model.getPrompt().renderElements()) {
				Dimension buttonDimensions = TBSGraphics.get2DStringBounds(g2,
						items);
				int headerEnd = mainDropDown.x;
				int buttonWidth = buttonDimensions.width
						+ TBSGraphics.padding.width * 2;
				TBSGraphics.setSubDropDownWidth(type, buttonWidth);
				Rectangle subButton = new Rectangle(headerEnd - buttonWidth,
						TBSGraphics.buttonsHeight * (model.getCurrentDropDowns().indexOf(type)+1),
						buttonWidth, TBSGraphics.buttonsHeight);
				Rectangle subButtons = new Rectangle(subButton.x, subButton.y,
						subButton.width, 0);
				for (SubDropDown item : items) {
					TBSGraphics.renderButtonBackground(g2, subButton, false);
					TBSGraphics.drawCenteredString(g2, item.toString(),
							subButton.x, subButton.y, subButton.width,
							subButton.height);
					g2.draw(subButton);
					subButtons.setSize(subButtons.width, subButtons.height
							+ subButton.height);
					subButton.setLocation(subButton.x, subButton.y
							+ subButton.height);
				}
				if (subButtons.height == 0)
					subDropDowns.put(type, new Rectangle());
				else
					subDropDowns.put(type, subButtons);
			}
		}
	}

	/**
	 * Draw the statusString.
	 */
	public void renderScreenString(Graphics2D g2) {
		if (!getScreenPrintMode()) {
			String screenString = null;
			for (OptimalHulls oh : model.getOptimalHulls(true)) {
				if (oh.getDisplay()) {
					screenString = MessageFormat.format(oh.getText(),model.getStudent().getName()+"'s");
					break;
				}
			}
			if (screenString == null) {
				TBSButtonType buttonClicked = model.getController()
						.getButtonClicked();
				if (buttonClicked == null || model.getPrompt() == null)
					buttonClicked = TBSButtonType.TREE;

				Properties adminProps = PropertyLoader.getProperties("admin");
				MessageFormat str = new MessageFormat(adminProps.getProperty(buttonClicked.name()));
				Object[] args = new Object[]{"",""};
				args[0] = model.getStudent().getName()+"'s";
				if (TBSButtonType.TREE.equals(buttonClicked)) {
					String lastUpdate = model.getStudent().getLastUpdate();
					if (lastUpdate != null && lastUpdate.length() > 0)
						args[1] = MessageFormat.format(adminProps.getProperty("lastUpdate"), lastUpdate);
				}
				screenString = str.format(args);
			}

			int yStep = TBSGraphics.buttonsHeight;
			int studentWidth = TBSGraphics.maxStudentNameWidth
					+ TBSGraphics.checkWidth + TBSGraphics.arrowWidth
					+ +getVerticalBar().getWidth()
					+ (hasStudentScroll ? studentBar.getWidth() : 0);
			int width = model.getApplet().getWidth() - studentWidth;
			int x = (model.getApplet().getWidth() - studentWidth) / 2
					+ (studentWidth - getVerticalBar().getWidth());

			List<String> lines = TBSGraphics.breakStringByLineWidth(g2,
					screenString, width);
			int yVal = model.getApplet().getHeight()
					- (TBSGraphics.buttonsHeight * (lines.size() + 1));
			for (String line : lines) {
				Dimension d = TBSGraphics.getStringBounds(g2, line);
				TBSGraphics.drawCenteredString(g2, line, x - (d.width / 2),
						yVal, d.width, yStep, TBSGraphics.emptyNodeColor);
				yVal += yStep;
			}
		}
	}

	private void renderScreenPrintText(Graphics2D g2) {
		int width = getWidth();
		if (getHorizontalBar().isVisible())
			width *= getHorizontalBar().getMaximum()
					/ getHorizontalBar().getVisibleAmount();

		TBSButtonType buttonClicked = model.getController().getButtonClicked();
		if (buttonClicked == null || model.getPrompt() == null)
			buttonClicked = TBSButtonType.TREE;
		
		Properties adminProps = PropertyLoader.getProperties("admin");
		MessageFormat str = new MessageFormat(adminProps.getProperty(buttonClicked.name()));
		Object[] args = new Object[]{"",""};
		args[0] = model.getStudent().getName()+"'s";
		if (TBSButtonType.TREE.equals(buttonClicked)) {
			String lastUpdate = model.getStudent().getLastUpdate();
			if (lastUpdate != null && lastUpdate.length() > 0)
				args[1] = MessageFormat.format(adminProps.getProperty("lastUpdate"), lastUpdate);
		}
		StringBuffer screenString = new StringBuffer(str.format(args));
		List<String> lines = TBSGraphics.breakStringByLineWidth(g2,
				screenString.toString(), width);
		int yVal = TBSGraphics.padding.height;
		for (String line : lines) {
			TBSGraphics.drawCenteredString(g2, line, TBSGraphics.padding.width,
					yVal, width, TBSGraphics.textHeight
							+ TBSGraphics.padding.height, Color.BLACK);
			yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		}

		yVal = getMaxY() == 0 ? (3 * TBSGraphics.textHeight)
				: (getMaxY() + TBSGraphics.textHeight);
		Properties questionProps = PropertyLoader.getProperties("questions");

		TBSGraphics.drawCenteredString(g2, "Written Questions",
				TBSGraphics.padding.width, yVal, width,
				TBSGraphics.textHeight + 4, Color.BLACK);
		yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		for (OpenQuestionButtonType writtenQuestion : OpenQuestionButtonType
				.getWrittenButtons()) {
			for (String s : TBSGraphics.breakStringByLineWidth(g2,
					(writtenQuestion.ordinal() + 1)
							+ ") "
							+ questionProps.getProperty(writtenQuestion
									.getQuestionKey()), width
							- TBSGraphics.padding.width * 2)) {
				TBSGraphics.drawCenteredString(g2, s,
						TBSGraphics.padding.width, yVal, 0,
						TBSGraphics.textHeight + 4, Color.BLACK);
				yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			}
			yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			String writtenAnswer = model.getStudent().getResponse(
					writtenQuestion).getText();
			if (TBSUtils.isStringEmpty(writtenAnswer))
				writtenAnswer = "NO REPONSE";
			for (String s : TBSGraphics.breakStringByLineWidth(g2,
					writtenAnswer, width - TBSGraphics.padding.width * 2)) {
				TBSGraphics.drawCenteredString(g2, s,
						TBSGraphics.padding.width, yVal, 0,
						TBSGraphics.textHeight + 4, Color.BLACK);
				yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			}
			yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		}
		TBSGraphics.drawCenteredString(g2, "Tree Analysis",
				TBSGraphics.padding.width, yVal, width,
				TBSGraphics.textHeight + 4, Color.BLACK);
		yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		for (String s : TBSGraphics.breakStringByLineWidth(g2,
				"1) All organism nodes are"
						+ (model.getGraph().allOrganismsTerminal() ? " "
								: " not ") + "terminal.", width
						- TBSGraphics.padding.width * 2)) {
			TBSGraphics.drawCenteredString(g2, s, TBSGraphics.padding.width,
					yVal, 0, TBSGraphics.textHeight + 4, Color.BLACK);
			yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		}

		for (String s : TBSGraphics.breakStringByLineWidth(g2,
				"2) All organism nodes are"
						+ (model.outOfTreeElements().isEmpty() ? " " : " not ")
						+ "included.", width - TBSGraphics.padding.width * 2)) {
			TBSGraphics.drawCenteredString(g2, s, TBSGraphics.padding.width,
					yVal, 0, TBSGraphics.textHeight + 4, Color.BLACK);
			yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
		}
		List<String> collisionText = TBSUtils.collisonText(model);
		if (collisionText.isEmpty()) {
			for (String s : TBSGraphics
					.breakStringByLineWidth(
							g2,
							"3) None of the groups of organisms collide with another group of organisms.",
							width - TBSGraphics.padding.width * 2)) {
				TBSGraphics.drawCenteredString(g2, s,
						TBSGraphics.padding.width, yVal, 0,
						TBSGraphics.textHeight + 4, Color.BLACK);
				yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			}
		} else {
			for (String s : TBSGraphics
					.breakStringByLineWidth(
							g2,
							"3) There were the following collisions between organism groups:",
							width - TBSGraphics.padding.width * 2)) {
				TBSGraphics.drawCenteredString(g2, s,
						TBSGraphics.padding.width, yVal, 0,
						TBSGraphics.textHeight + 4, Color.BLACK);
				yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			}
			for (String s : collisionText) {
				TBSGraphics.drawCenteredString(g2, s,
						TBSGraphics.padding.width, yVal, 0,
						TBSGraphics.textHeight + 4, Color.BLACK);
				yVal += TBSGraphics.textHeight + TBSGraphics.padding.height;
			}
		}
	}
}
